/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
 *
 */

import { MarkEventType, MetricFactory, Project } from '@ohos/hvigor';
import path from 'path';
import util from 'util';

import { findTargetProduct } from '../../common/find-target-product.js';
import { ProjectPathInfoIml } from '../../common/iml/project-path-info-iml.js';
import { ohosTrace } from "../../common/trace/ohos-trace.js";
import { BuildArtifactExtension } from '../../const/build-directory-const.js';
import { CommonConst, DefaultTargetConst } from '../../const/common-const.js';
import { RuntimeTypeEnum } from '../../enum/runtime-type-enum.js';
import { ProjectModel } from '../../model/project/project-model.js';
import { BuildOpt } from '../../options/build/build-opt.js';
import { ProjectBuildProfile } from '../../options/build/project-build-profile.js';
import { AbstractHapModulePlugin } from '../../plugin/common/abstract-hap-module-plugin.js';
import { OhosPluginId } from '../../plugin/common/ohos-plugin-id.js';
import { HspPlugin } from '../../plugin/hsp-plugin';
import { buildOptionManager } from '../../project/build-option/build-mode-manager.js';
import { ProjectExtraInfoService } from '../../project/project-extra-info-service.js';
import { SdkInfo } from '../../sdk/sdk-info.js';
import { SdkLoader } from '../../sdk/sdk-loader.js';
import { getElementFromArr } from '../../utils/array-util.js';
import { OhosLogger } from '../../utils/log/ohos-logger.js';
import { ModuleTargetData } from '../data/hap-task-target-data.js';

import { ModuleTaskService } from './module-task-service.js';
import { TaskService } from './task-service.js';
import ProductBuildOpt = ProjectBuildProfile.ProductBuildOpt;
import { getAlignTarget } from '@ohos/hvigor/src/base/util/task-util';

import { DependencyManager } from '../../project/dependency/dependency-manager';

import { GlobalProjectDataService } from './global-project-data-service.js';

const ohosTestTargetName = DefaultTargetConst.OHOS_TEST_TARGET;

/**
 * 基于持久化project的模型层提供的数据，经过处理后,提供给打包app任务流需要使用的服务和数据
 *
 * @since 2022/1/20
 */
export class ProjectTaskService extends TaskService {
  private _log: OhosLogger = OhosLogger.getLogger(ProjectTaskService.name);
  private _targetProduct: ProductBuildOpt;
  private readonly _pathInfo: ProjectPathInfoIml;
  private readonly _sdkInfo: SdkInfo;
  private readonly _buildOption: BuildOpt;

  // app级别的每个product对应的数据
  private readonly _productDataMap: Map<string, ModuleTargetData[]>;

  constructor(project: Project, projectModel: ProjectModel, dependencyManager: DependencyManager, isFaMode: boolean) {
    super(project, projectModel, dependencyManager, isFaMode);
    this._targetProduct = findTargetProduct(projectModel!);
    const runtimeOS = ProjectExtraInfoService.getProjectRuntimeOS(projectModel);
    const isOhos: boolean = runtimeOS === RuntimeTypeEnum.OpenHarmony;

    const apiMeta: ProjectBuildProfile.ApiMeta = projectModel.getCompileApiMetaByProduct(this._targetProduct.name);
    this._sdkInfo = SdkLoader.getSdkInfo(apiMeta, isOhos);
    this._pathInfo = new ProjectPathInfoIml(projectModel, this._targetProduct.name);
    this._productDataMap = new Map<string, ModuleTargetData[]>();
    this._buildOption = buildOptionManager.getProductBuildOption(this._targetProduct.name)!;
    GlobalProjectDataService.getInstance().initGlobalProjectData(projectModel);

    ohosTrace.traceNativeCompiler(this._buildOption.nativeCompiler);
  }

  async setup() {
    await this._sdkInfo.setup();
    process.env['OHOS_SDK_NATIVE'] = this._sdkInfo.getSdkNativeDir();
    const markEvent = MetricFactory.createMarkEvent('sdkVersion', this._sdkInfo.getToolchainsComponentVersion() ?? '');
    markEvent.setMarkType(MarkEventType.OTHER);
    const caseSensitiveCheck = this._targetProduct.buildOption?.strictMode?.caseSensitiveCheck;
    if (caseSensitiveCheck) {
      const caseSensitiveCheckMarkEvent = MetricFactory.createMarkEvent('caseSensitiveCheckOn', 'caseSensitiveCheck check is on');
      caseSensitiveCheckMarkEvent.setMarkType(MarkEventType.OTHER);
    } else {
      const caseSensitiveCheckMarkEvent = MetricFactory.createMarkEvent('caseSensitiveCheckOff', 'caseSensitiveCheck check is off');
      caseSensitiveCheckMarkEvent.setMarkType(MarkEventType.OTHER);
    }
  }

  getSdkInfo() {
    return this._sdkInfo;
  }

  refreshData() {
    this._productDataMap.clear();
    this.initProductData();
    this._targetProduct = findTargetProduct(this._projectModel);
  }

  /**
   * 初始化app模块打包流的product数据集合
   */
  public initProductData(): void {
    const project = this.hvigorNode as Project;
    project?.getSubModules().forEach((value) => {
      const moduleName = value.getName();
      const plugin = value.getPluginById(OhosPluginId.OHOS_HAP_PLUGIN) ?? value.getPluginById(OhosPluginId.OHOS_HSP_PLUGIN);
      const moduleTargetDataArr: ModuleTargetData[] = [];
      if (plugin !== undefined && (plugin instanceof AbstractHapModulePlugin || plugin instanceof HspPlugin)) {
        // 需要获取在根项目下的build-profile里配了哪些target,没配置的就不需要打包
        const appModuleOpt = this._projectModel?.getModuleProfileOpt(moduleName);
        const appModuleConfigTargets = appModuleOpt?.targets;
        const targetDataSet = (plugin.getTaskService() as ModuleTaskService).getTargetDataSet();
        const targetNameSet: Set<string> = new Set<string>();
        targetDataSet.forEach((value) => {
          targetNameSet.add(value[0].getTargetName());

          // 工程级目录build-profile中product配置了target
          const isApplyProductTarget = getElementFromArr(appModuleConfigTargets, value[0].getTargetName()) !== undefined;

          // 若没有配置，则无需后续添加打包、校验等操作
          if (!isApplyProductTarget) {
            return;
          }

          // 校验OhosTestTarget
          this.checkIsOhosTestTarget(value[0]);

          // 该target需要打包
          if (value[1]) {
            moduleTargetDataArr.push(value[0]);
          }
        });
        this.checkTargetIsUnknown(targetNameSet, appModuleConfigTargets);

        // 只有当该module有target需要打包时才加入到该map中
        if (moduleTargetDataArr.length !== 0) {
          this._productDataMap.set(moduleName, moduleTargetDataArr);
        }
      }
    });
  }

  getProductDataMap(): Map<string, ModuleTargetData[]> {
    return this._productDataMap;
  }

  getPathInfo(): ProjectPathInfoIml {
    return this._pathInfo;
  }

  getTargetProduct(): ProductBuildOpt {
    return this._targetProduct;
  }

  getBuildOption(): BuildOpt {
    return this._buildOption;
  }

  getAppOutputFileName(isSigned = false): string {
    const signedSuffix = isSigned ? 'signed' : 'unsigned';
    const projectName = this._projectModel.getName();
    const outputName = this._targetProduct.output?.artifactName;
    const appOutputFileName = outputName ? `${outputName}${isSigned ? '' : '-unsigned'}` : `${projectName}-${this._targetProduct.name}-${signedSuffix}`;
    return `${appOutputFileName}${BuildArtifactExtension.DOT_APP}`;
  }

  /**
   * 检查Product是否配置了ohosTarget
   * ohosTarget不需要打包进App中
   *
   * @param {ModuleTargetData} moduleTargetData moduleTarget对象
   */
  checkIsOhosTestTarget(moduleTargetData: ModuleTargetData): void {
    if (moduleTargetData.getTargetName() !== ohosTestTargetName) {
      return;
    }
    const moduleName = moduleTargetData.getModuleModel().getModule().getName();

    this._log.printErrorExit(
        'OHOS_TEST_CAN_NOT_APPLY_TO_PRODUCT',
      [this._projectModel.getProfilePath()],
      [[moduleName]]
    );
  }

  /**
   * 检查是否存在未定义的target
   *
   * @param targetNameSet 模块已定义的target集合
   * @param appModuleConfigTargets 工程级build-profile.json5中的targets标签
   */
  private checkTargetIsUnknown(targetNameSet: Set<string>, appModuleConfigTargets: ProjectBuildProfile.ProjectTargetBuildOpt[] | undefined): void {
    if (!appModuleConfigTargets || getAlignTarget()) {
      return;
    }

    for (const appModuleConfigTarget of appModuleConfigTargets) {
      if (!targetNameSet.has(appModuleConfigTarget.name)) {
        this._log
          .printErrorExit(
            'UNKNOWN_TARGET_IN_PROJECT_BUILD_PROFILE',
            [appModuleConfigTarget.name, path.resolve(this._projectModel?.getProjectDir(), CommonConst.PROFILE_JSON5)],
            [[util.format(appModuleConfigTarget)]]
          );
      }
    }
  }
}
